/**
 * @author Gregory Tardivel
 */
var GT = { REVISION: '1.0' };

(function() {

}());

/** Animations. */
GT.walk = 'walk';
GT.idle = 'idle';

/** Jeu.
 * @param pPseudo Nom du joueur
 * @param pSocket Socket.io Node.js */
GT.Game= function(pPseudo, pSocket) {
	this.clock = new THREE.Clock();
	this.pseudo = pPseudo;
	this.socket = pSocket;
	this.screen = {w: window.innerWidth, h: window.innerHeight};
	this.terrain = null;
	this.player = null;
	this.otherPlayers = {};
	this.iaPlayers = {};
	// SCENE
	this.scene = new THREE.Scene();
	// CAMERA
	this.camera = new THREE.PerspectiveCamera(45, this.screen.w / this.screen.h, 0.1, 50000);
	this.camera.position.set(0, 40, -100);
	this.scene.add(this.camera);
	// CLAVIER
	this.keyboard = new GT.Keyboard();
	// RENDERER
	this.renderer = new THREE.WebGLRenderer({ antialias: true });
	this.renderer.setSize(this.screen.w, this.screen.h);
	$('body').append(this.renderer.domElement);
	// LIGHT
	var light = new THREE.DirectionalLight(0xffffff, 0.8);
	light.position.set(0, 500, 0).normalize();
	this.scene.add(light);
	// SKYBOX/FOG
	var skyBoxGeometry = new THREE.CubeGeometry(10000, 10000, 10000);
	var skyBoxMaterial = new THREE.MeshBasicMaterial({ color: 0x9999ff, side: THREE.BackSide });
	var skyBox = new THREE.Mesh(skyBoxGeometry, skyBoxMaterial);
	this.scene.add(skyBox);
	//scene.fog = new THREE.FogExp2( 0x9999ff, 0.00025 );
	// LIGHT
	var ambientLight = new THREE.AmbientLight(0x111111);
	this.scene.add(ambientLight);
	// EVENTS
	var _this = this;
	$(window).resize(function(event) {
		event.preventDefault();
		_this.screen.w  = window.innerWidth;
		_this.screen.h = window.innerHeight;
		_this.renderer.setSize(_this.screen.w, _this.screen.h);
		_this.camera.aspect = _this.screen.w / _this.screen.h;
		_this.camera.updateProjectionMatrix();
	});
	// Loader JSON
	this.loader = new THREE.JSONLoader();
	// GUI
	this.gui = new GT.GUI();
	// Menu
	for (var i = 0; 10 > i; i++) {
		var menu = new GT.Menu('sort' + i, '50px', '50px', 'sorts');
		menu.css('left', parseInt(60 + i * 61) + 'px');
		this.gui.add(menu);
	}
	// Minimap
	this.minimap = new GT.Minimap('/textures/map1.png', {'width': 512, 'height': 512}, '150px', '150px');
	this.gui.add(this.minimap);
	// Image du joueur
	var hplayer = new GT.Menu('myhead', '50px', '50px', 'player');
	this.gui.add(hplayer);
	// Barre de vie
	var playerhealth = new GT.Menu('myhealth', '150px', '15px', 'barres');
	playerhealth.css('top', '15px');
	playerhealth.css('background-color', '#EF0000');
	this.gui.add(playerhealth);
	// Barre de mana
	var playermana = new GT.Menu('mymana', '150px', '15px', 'barres');
	playermana.css('top', '35px');
	playermana.css('background-color', '#0000EF');
	this.gui.add(playermana);
	// Chat
	this.chatbox = new GT.Chat(this.keyboard, this.pseudo, this.socket);
	this.chatbox.load();
	this.ready = false;
	$(document).ready(function() {
		_this.animate();
		_this.ready = true;
	});
}
GT.Game.prototype.ready = function(callback) {
	if (this.ready) {
		callback.call();
	}
}
GT.Game.prototype.addIA = function(iaPlayer) {
	this.iaPlayers[iaPlayer.name] = iaPlayer;
}
GT.Game.prototype.loadTerrain = function(model, texture) {
	var _this = this;
	this.loader.load(model, function(geometry) {
		var material = new THREE.MeshPhongMaterial({
			vertexColors: THREE.NoColors, shading: THREE.SmoothShading, map: new GT.Texture(texture)
		});
		var mesh = new THREE.Mesh(geometry, material);
		_this.scene.add(mesh);
		_this.terrain = new GT.Terrain(mesh);
	});
}
GT.Game.prototype.loadPlayer = function(model, textures) {
	var _this = this;
	this.loader.load(model, function(geometry) {
		var material = new THREE.MeshPhongMaterial({
			color: 0xffffff,
			specular: 0x333333,
			shininess: 20,
			wrapAround: true,
			morphTargets: true,
			morphNormals: true,
			vertexColors: THREE.NoColors,
			shading: THREE.SmoothShading,
			map: new GT.Texture(textures.body) 
		}, {
			color: 0xffffff,
			specular: 0x333333,
			shininess: 20,
			wrapAround: true,
			morphTargets: true,
			morphNormals: true,
			vertexColors: THREE.NoColors,
			shading: THREE.SmoothShading,
			map: new GT.Texture(textures.eyes)
		});
		geometry.computeFaceNormals();
		geometry.computeMorphNormals();
		var mesh = new THREE.MorphAnimMesh(geometry, material);
		_this.scene.add(mesh);
		_this.player = new GT.Character(_this.pseudo, mesh, _this.camera, {
			position: {x: 0, y: 0, z: 0},
			rotation: {x: 0, y: 0, z: 0},
			animation: null
		});
		_this.player.addCollidable(_this.terrain);
	});
}
GT.Game.prototype.loadPlayers = function(players) {
	var _this = this;
	$.each(players, function(key, item) {
		if (_this.pseudo != key && item.connected) {
			// Chargement du personnage
			_this.loadOtherCharacter(key, {
				position: {x: item.position.x, y: item.position.y, z: item.position.z},
				rotation: {x: item.rotation.x, y: item.rotation.y, z: item.rotation.z},
				animation: item.animation
			}, item.model, item.textures);
		}
	});
}
GT.Game.prototype.newPlayer = function(name, model, textures) {
	this.chatbox.write(name, ' a rejoint le jeu !');
	this.loadOtherCharacter(name, {
		position: {x: 0, y: 0, z: 0},
		rotation: {x: 0, y: 0, z: 0},
		animation: null
	}, model, textures);
}
GT.Game.prototype.exitPlayer = function(name) {
	this.chatbox.write(name, ' a quitté jeu !');
	// Suppression du personnage de la scene
	var cible = this.scene.getObjectByName(name);
	if (cible) {
		this.scene.remove(cible);
	}
	this.otherPlayers[name] = null;
}
/** Chargement d'un autre personnage.
 * @param name Nom du personnage
 * @param data Contient la position, le quaternion et l'animation en cours
 * @param model Model THREE.js
 * @param textures Textures du model {body: '', eyes: ''} */
GT.Game.prototype.loadOtherCharacter = function(name, data, model, textures) {
	// OTHER CHARACTER
	var _this = this;
	this.loader.load(model, function(geometry) {
		var material = new THREE.MeshPhongMaterial({
				color: 0xffffff, 
				specular: 0x333333, 
				shininess: 20, 
				wrapAround: true, 
				morphTargets: true, 
				morphNormals: true,
				vertexColors: THREE.NoColors, 
				shading: THREE.SmoothShading, 
				map: new GT.Texture(textures.body)
			},{
				color: 0xffffff,
				specular: 0x333333, 
				shininess: 20, 
				wrapAround: true, 
				morphTargets: true, 
				morphNormals: true,
				vertexColors: THREE.NoColors, 
				shading: THREE.SmoothShading, 
				map: new GT.Texture(textures.eyes)
		});
		geometry.computeFaceNormals();
		geometry.computeMorphNormals();
		var mesh = new THREE.MorphAnimMesh(geometry, material);
		_this.scene.add(mesh);
		_this.otherPlayers[name] = new GT.Character(name, mesh, _this.camera, data);
	});
}
GT.Game.prototype.animate = function() {
	if (game) {
		requestAnimationFrame(game.animate);
		game.render();
		game.update();
	} else if (this instanceof GT.Game) {
		requestAnimationFrame(this.animate);
		this.render();
		this.update();
	}
}
GT.Game.prototype.update = function() {
	TWEEN.update();
	// delta = change in time since last call (seconds)
	var delta = this.clock.getDelta();
	if (this.player) {
		if (this.terrain) {
			var direction = this.keyboard.keyDirection();
			direction.multiplyScalar(delta);
			if (direction.z != 0) {
				// Animation : walk
				this.player.setAnimation(GT.walk);
				this.socket.emit('animate_joueur', GT.walk);
			} else {
				this.player.setAnimation(null);
				this.socket.emit('animate_joueur', null);
			}
			if (direction.x != 0 || direction.z != 0) {
				// Deplacement
				this.socket.emit('move_joueur', {x: direction.x, y: direction.y, z: direction.z});
				this.player.move(direction);
				// Nouvelle position
				this.socket.emit('position_joueur', {x: this.player.mesh.position.x, y: this.player.mesh.position.y, z: this.player.mesh.position.z});
			}
			var rotation = this.keyboard.keyDirection();
			rotation.multiplyScalar(delta);
			if (rotation.y != 0) {
				// Rotation
				this.socket.emit('rotate_joueur', {x: rotation.x, y: rotation.y, z: rotation.z});
				this.player.rotate(rotation);
				// Nouvelle rotation
				this.socket.emit('rotation_joueur', {x: this.player.mesh.rotation.x, y: this.player.mesh.rotation.y, z: this.player.mesh.rotation.z});
			}
		}
		this.player.update();
		var _this = this;
		$.each(this.iaPlayers, function(key, item) {
			if (_this.iaPlayers[key]) {
				_this.iaPlayers[key].update(_this.player, delta);
			}
		});
	}
	this.gui.update(this.terrain, this.player);
}
GT.Game.prototype.render = function() {
	if (this.player) {
		this.player.animate();
	}
	var _this = this;
	$.each(this.otherPlayers, function(key, item) {
		if (_this.otherPlayers[key]) {
			_this.otherPlayers[key].animate();
		}
	});
	$.each(this.iaPlayers, function(key, item) {
		if (_this.iaPlayers[key] && _this.iaPlayers[key].character) {
			_this.iaPlayers[key].character.animate();
		}
	});
	this.renderer.render(this.scene, this.camera);
}

/** Intelligence artificielle.
 * @param game Jeu GT.Game
 * @param pName Nom
 * @param model Model THREE.js
 * @param textures Textures {body: '', eyes: ''}
 * @param pQuete Quête liée au personnage
 * @param position Position du personnage
 * @param txtContener Conteneur des messages */
GT.IA = function(game, pName, model, textures, pQuete, position, txtContener) {
	this.destination = null;
	this.character = null;
	this.name = pName;
	this.quete = pQuete;
	this.rays = [
		new THREE.Vector3(1, 0, 0), new THREE.Vector3(-1, 0, 0),
		new THREE.Vector3(1, 0, 1), new THREE.Vector3(-1, 0, 1),
		new THREE.Vector3(1, 0, -1), new THREE.Vector3(-1, 0, -1),
		new THREE.Vector3(0, 0, 1), new THREE.Vector3(0, 0, -1)
	];
	var _this = this;
	this.loader = new THREE.JSONLoader();
	this.loader.load(model, function(geometry) {
		var material = new THREE.MeshPhongMaterial({
			color: 0xffffff,
			specular: 0x333333,
			shininess: 20,
			wrapAround: true,
			morphTargets: true,
			morphNormals: true,
			vertexColors: THREE.NoColors,
			shading: THREE.SmoothShading,
			map: new GT.Texture(textures.body) 
		}, {
			color: 0xffffff,
			specular: 0x333333,
			shininess: 20,
			wrapAround: true,
			morphTargets: true,
			morphNormals: true,
			vertexColors: THREE.NoColors,
			shading: THREE.SmoothShading,
			map: new GT.Texture(textures.eyes)
		});
		geometry.computeFaceNormals();
		geometry.computeMorphNormals();
		var mesh = new THREE.MorphAnimMesh(geometry, material);
		game.scene.add(mesh);
		_this.character = new GT.Character(pName, mesh, null, {
			position: position,
			rotation: {x: 0, y: 0, z: 0},
			animation: null
		});
		_this.character.addCollidable(game.terrain);
		_this.character.setTextContener(txtContener);
	});
}
GT.IA.prototype.setDestination = function(pDestination) {
	this.destination = pDestination;
}
GT.IA.prototype.lookAt = function(vector) {
	// Rotation du personnage vers la destination
	var forward = new THREE.Vector3(0, 0, 1);
	forward.transformDirection(this.character.mesh.matrixWorld);
	var angle = forward.angleTo(vector);
	this.character.mesh.rotateY(angle);
}
GT.IA.prototype.walk = function(dir, delta) {
	this.character.setAnimation('walk');
	var direction = new THREE.Vector3(0, 0, dir).normalize();
	direction.multiplyScalar(delta);
	this.character.move(direction);
}
GT.IA.prototype.update = function(player, delta) {
	if (this.contact(player.mesh)) {
		this.character.setAnimation(null);
		this.character.talk(this.quete.show());
		this.character.talk('Bonjour');
	} else if (this.destination) {
		// Calcul de la direction du personnage vers la destination	en Z
		var diffZ = 0;
		if (this.destination.z > 0) {
			diffZ = this.destination.z - this.character.mesh.position.z;
		} else if (this.destination.z < 0) {
			diffZ = this.character.mesh.position.z - this.destination.z;
		}
		
		if (Math.abs(diffZ) > 1) {
			this.lookAt(this.destination);
			this.walk(diffZ, delta);
		} else {
			this.character.setAnimation(null);
			// Calcul de la direction du personnage vers la destination	en X
			var diffX = 0;
			if (this.destination.x > 0) {
				diffX = this.destination.x - this.character.mesh.position.x;
			} else if (this.destination.x < 0) {
				diffX = this.character.mesh.position.x - this.destination.x;
			}
			if (Math.abs(diffX) > 1) {
				this.lookAt(new THREE.Vector3(this.destination.x, 0, 0));
				this.walk(diffX, delta);
			} else {
				this.character.setAnimation(null);
			}
		}
	}
}
GT.IA.prototype.contact = function(mesh) {
	var contactZ = this.character.contact(mesh, new THREE.Vector3(0, 0, 1)) < 20 
		|| this.character.contact(mesh, new THREE.Vector3(0, 0, -1)) < 20;
	var contactX = this.character.contact(mesh, new THREE.Vector3(1, 0, 0)) < 20 
		|| this.character.contact(mesh, new THREE.Vector3(-1, 0, 0)) < 20;
	var contactXZ = this.character.contact(mesh, new THREE.Vector3(1, 0, 1)) < 20 
		|| this.character.contact(mesh, new THREE.Vector3(1, 0, -1)) < 20
		|| this.character.contact(mesh, new THREE.Vector3(-1, 0, 1)) < 20
		|| this.character.contact(mesh, new THREE.Vector3(-1, 0, -1)) < 20;
	return contactX || contactZ || contactXZ;
}

/** Quête. */
GT.Quete = function(pTitre, pObjectif) {
	this.titre = pTitre;
	this.objectif = pObjectif;
	this.avancement = 0;
	// TODO
}
GT.Quete.prototype.show = function() {
	// TODO
	return this.titre;
}

/** Objectif. */
GT.Objectif = function() {
	// TODO
}

/** Interface graphique. */
GT.GUI = function() {
    this.menus = [];
}
GT.GUI.prototype.add = function(menu) {
    this.menus.push(menu);
}
GT.GUI.prototype.update = function(terrain, player) {
    $(this.menus).each(function() {
        if (this instanceof GT.Menu) {
            this.render();
        } else if (this instanceof GT.Minimap) {
            this.render(terrain, player);
        }
    });
}

/** Menu graphique.
 * @param pName Nom du menu
 * @param pWidth Largeur du menu au format CSS
 * @param pHeight Hauteur du menu au format CSS
 * @param pClass Classe CSS */
GT.Menu = function(pName, pWidth, pHeight, pClass) {
    this.name = pName;
    this.width = pWidth;
    this.height = pHeight;
    this.cssClass = pClass;
    this.properties = {};
    this.display = true;
}
GT.Menu.prototype.css = function(prop, value) {
    this.properties[prop] = value;
}
GT.Menu.prototype.render = function() {
    var div = 'div[name="' + this.name + '"]';
    if (this.display) {
        if (!$(div)[0]) {
            $('body').append('<div name="' + this.name + '" class="' + this.cssClass + '"></div>');
            $(div).css({
                'width': this.width,
                'height': this.height
            });
            var _this = this;
            $(div).click(function(event) {
                event.preventDefault();
                _this.action(event);
            });
        }
        $(div).css(this.properties);
    } else {
        $('body').remove($(div));
    }
}
GT.Menu.prototype.action = function(event) {
    // TODO
}

/** Chat.
 * @param pKeyboard Clavier de type GT.Keyboard
 * @param pSocket Socket.io */
GT.Chat = function(pKeyboard, pPseudo, pSocket) {
    this.keyboard = pKeyboard;
	this.socket = pSocket;
	this.pseudo = pPseudo;
    var _this = this;
    $(document).keydown(function(event) {
        _this.viewChange(event);
    });
}
GT.Chat.prototype.viewChange = function(event) {
    var _this = this;
    if (event.keyCode == 13 || event.keyCode == 27) {
        // ENTER ou ESCAPE
		if (event.keyCode == 13) {
			this.socket.emit('nouveau_message', $('input[name="chattxt"]').val());
			// Affiche le message sur la page de l'emetter
			_this.write(_this.pseudo, $('input[name="chattxt"]').val());
		}
        $('input[name="chattxt"]').hide();
        $('input[name="chattxt"]').val('');
        $(document).bind('keydown', function(event) {
            _this.keyboard.keyChange(event);
            _this.viewChange(event);
        });
        $(document).bind('keyup', function(event) {
            _this.keyboard.keyChange(event);
        });
    } else if (event.keyCode == 9) {
        // TABULATION
        this.keyboard.keycodes = {};
        $('input[name="chattxt"]').show();
        $('input[name="chattxt"]').focus();
        $(document).unbind('keydown');
        $(document).unbind('keyup');
        $(document).keydown(function(event) {
            _this.viewChange(event);
        });
    }
}
GT.Chat.prototype.write = function(pseudo, message) {
	var now = new Date().toLocaleString();
    $('div[name="chatbox"]').prepend('<p class="message">' + now + ' - <span>[' + pseudo + '] :</span> ' + message + '</p>');
}
GT.Chat.prototype.load = function() {
    var div = 'div[name="chatform"]';
    if (!$(div)[0]) {
        $('body').append('<div name="chatform" class="chat"></div>');
        $(div).append('<div name="chatbox"></div>');
        $(div).append('<input type="text" name="chattxt" class="chattxt" value="">');
    }
}

/** Mini map.
 * @param pImage Image de la map
 * @param pImageProperties Propriétés de l'image de la map
 * @param pWidth Largeur du menu au format CSS
 * @param pHeight Hauteur du menu au format CSS */
GT.Minimap = function(pImage, pImageProperties, pWidth, pHeight) {
    this.image = 'url("' + pImage + '")';
    this.imgProperties = pImageProperties;
    this.width = pWidth;
    this.height = pHeight;
    this.display = true;
}
GT.Minimap.prototype.render = function(terrain, player) {
    var div = 'div[name="miniMap"]';
    if (this.display) {
        if (!$(div)[0]) {
            $('body').append('<div name="miniMap" class="minimap"></div>');
            $(div).css({
                'width': this.width,
                'height': this.height,
                'background-image': this.image
            });
        }
        if (player) {
            var wmap = $(div).width();
            var hmap = $(div).height();
            var wtxt = this.imgProperties['width'];
            var htxt = this.imgProperties['height'];
            var xratio = wtxt / terrain.xwidth;
            var zratio = htxt / terrain.zwidth;
            var centerx = (terrain.center.x + player.mesh.position.x) * xratio;
            var centerz = (terrain.center.z + player.mesh.position.z) * zratio;
            var posx = parseInt((wmap - wtxt) / 2 - centerx);
            var posz = parseInt((hmap - htxt) / 2 - centerz);
            $(div).css('background-position', posx + 'px ' + posz + 'px');
        }
    } else {
        $('body').remove($(div));
    }
}

/** Camera. */
GT.Camera = function() {
}

/** Clavier. */
GT.Keyboard = function() {
    this.keycodes = {};
    this.shift = false;
    this.ctrl = false;
    this.alt = false;

    var _this = this;
    $(document).keydown(function(event) {
        _this.keyChange(event);
    });
    $(document).keyup(function(event) {
        _this.keyChange(event);
    });
    this.vectorX = {'+': 'A', '-': 'E'};
    this.vectorY = {'+': 'Q', '-': 'D'};
    this.vectorZ = {'+': 'Z', '-': 'S'};
}
GT.Keyboard.prototype.defineDirection = function(vecx, vecy, vecz) {
    this.vectorX = vecx;
    this.vectorY = vecy;
    this.vectorZ = vecz;
}
GT.Keyboard.prototype.keyDirection = function() {
    var direction = new THREE.Vector3(0, 0, 0);
    if (this.pressed(this.vectorX['+'])) {
        direction.x = 1;
    }
    if (this.pressed(this.vectorX['-'])) {
        direction.x = -1;
    }
    if (this.pressed(this.vectorY['+'])) {
        direction.y = 1;
    }
    if (this.pressed(this.vectorY['-'])) {
        direction.y = -1;
    }
    if (this.pressed(this.vectorZ['+'])) {
        direction.z = 1;
    }
    if (this.pressed(this.vectorZ['-'])) {
        direction.z = -1;
    }
	if (this.shift) {
		direction.multiplyScalar(4);
	}
    return direction;
}
GT.Keyboard.prototype.text = function() {
    var retour = '';
    $(this.pressed).each(function(key, value) {
        if (value) {
            retour += String.fromCharCode(97 + key);
        }
    });
    return retour;
}
GT.Keyboard.prototype.keyChange = function(event) {
	event.preventDefault();
    // Touche
    var pressed = event.type == 'keydown';
    this.keycodes[event.keyCode] = pressed;
    // SHIFT
    this.shift = event.shiftKey;
    // CTRL
    this.ctrl = event.ctrlKey;
    // ALT
    this.alt = event.altKey;
}
GT.Keyboard.prototype.pressed = function(keys) {
    var key, code, pressed = false;
    for (var i = 0; i < keys.length; i++) {
        key = keys[i];
        if (key == 'shift') {
            pressed = this.shift;
        } else if (key == 'ctrl') {
            pressed = this.ctrl;
        } else if (key == 'alt') {
            pressed = this.alt;
        } else {
            pressed = this.keycodes[key.toUpperCase().charCodeAt(0)];
        }
    }
    return pressed;
}

/** Texture. */
GT.Texture = function(path) {
	var texture = THREE.ImageUtils.loadTexture(path);
	texture.wrapS = THREE.RepeatWrapping;
	texture.wrapT = THREE.RepeatWrapping;
	texture.repeat.set(1, 1);
	return texture;
}

/** Terrain. */
GT.Terrain = function(pMesh) {
    this.mesh = pMesh;
    this.xwidth = 0;
    this.zwidth = 0;
    this.yheight = 0;
    this.center = this.mesh.position.clone();
    this.mesh.geometry.computeBoundingBox();
    var box = this.mesh.geometry.boundingBox;
    if (box) {
        this.xwidth = Math.abs(box.min.x) + Math.abs(box.max.x);
        this.zwidth = Math.abs(box.min.z) + Math.abs(box.max.z);
        this.yheight = Math.abs(box.min.y) + Math.abs(box.max.y);
    }
}

/** Personnage.
 * @param pName Nom du personnage
 * @param pMesh Mesh du personnage
 * @param pCamera Camera
 * @param pPosition Position {x, y, z}, Rotation {x, y, z} et Animation */
GT.Character = function(pName, pMesh, pCamera, pDatas) {
    /** Constructeur. */
    this.name = pName;
    this.mesh = pMesh;
	this.mesh.name = pName;
	this.mesh.position.set(pDatas.position.x, pDatas.position.y, pDatas.position.z);
	this.mesh.rotation.set(pDatas.rotation.x, pDatas.rotation.y, pDatas.rotation.z);
	this.animation = pDatas.animation;
	this.conteneurTxt = null;
    this.collidables = [];

    this.animations = {
        'walk': {offset: 1, duration: 1000, frames: 32},
        'idle': {offset: 0, duration: 1000, frames: 0}
    };

    this.caster = new THREE.Raycaster();
    this.caster.near = 2;
    this.caster.far = 100;

    this.lastKeyframe = 0;
    this.currentKeyframe = 0;

    // CAMERA
	if (pCamera) {
		this.camera = pCamera;
		this.relativeCameraOffset = this.camera.position.clone();
	}

    var _this = this;
    this.lookAround = false;
    $(document).mousedown(function(event) {
        _this.lookAround = true;
		console.log('Position: x=' + _this.mesh.position.x + ' y=' + _this.mesh.position.y + ' z=' + _this.mesh.position.z);
    })
    $(document).mouseup(function(event) {
        _this.lookAround = false;
    })
}
GT.Character.prototype.setTextContener = function(contener) {
	this.conteneurTxt = contener;
}
GT.Character.prototype.addCollidable = function(collidable) {
	this.collidables.push(collidable);
}
GT.Character.prototype.setAnimation = function(name) {
	this.animation = null;
	if (name) {
		this.animation = this.animations[name];
	}
}
GT.Character.prototype.update = function() {
	if (this.camera && !this.lookAround) {
		var copyRelative = this.relativeCameraOffset.clone();
		var cameraOffset = copyRelative.applyMatrix4(this.mesh.matrixWorld);
		// Camera TWEEN.
		new TWEEN.Tween(this.camera.position).to({x: cameraOffset.x, y: cameraOffset.y,z: cameraOffset.z }, 95 )
			.interpolation(TWEEN.Interpolation.Bezier)
			.easing(TWEEN.Easing.Sinusoidal.InOut).start();
		this.camera.lookAt(this.mesh.position);
	}
}
GT.Character.prototype.rotate = function(pDirection) {
    var rotateAngle = Math.PI / 4;
    var direction = pDirection.clone().multiplyScalar(rotateAngle);
    this.mesh.rotateY(direction.y);
}
GT.Character.prototype.contact = function(cmesh, direction) {
	direction.applyQuaternion(this.mesh.quaternion);
	this.caster.set(this.mesh.position, direction);
	intersects = this.caster.intersectObject(cmesh, false);
	var contact = this.caster.far;
	if (intersects.length > 0) {
		var distance = intersects[0].distance;
		contact = distance;
	}
	return contact;
}
GT.Character.prototype.move = function(pDirection) {
    for (var i = 0; i < this.collidables.length; i++) {
        var intersects = [];
        var objet = this.collidables[i];
        var cmesh = objet.mesh;

        // Collisions X et Z
        var globalDirection = pDirection.clone();
		if (this.contact(cmesh, globalDirection) < 5) {
			if (globalDirection.z != 0) {
                pDirection.z = 0;
            } else if (globalDirection.x != 0) {
                pDirection.x = 0;
            }
		}
        var moveDistance = 20;
        var direction = pDirection.clone().multiplyScalar(moveDistance);
        if (direction.z != 0) {
            this.mesh.translateZ(direction.z);
        }
        if (direction.x != 0) {
            this.mesh.translateX(direction.x);
        }
    }
}
GT.Character.prototype.animate = function() {
	if (this.collidables) {
		for (var i = 0; i < this.collidables.length; i++) {
			var intersects = [];
			var objet = this.collidables[i];
			var cmesh = objet.mesh;
			if (objet instanceof GT.Terrain) {
				// Gravité Y
				var down = 0;
				var gravity = this.mesh.position.clone();
				gravity.y += 50;
				this.caster.set(gravity, new THREE.Vector3(0, -1, 0));
				intersects = this.caster.intersectObject(cmesh, false);
				if (intersects.length > 0) {
					down = intersects[0].distance - 50;
				}
				this.mesh.translateY(-down);
			}
		}
	}
    if (this.mesh && this.animation) {
        var time = new Date().getTime() % this.animation.duration;
        var interpolation = this.animation.duration / this.animation.frames;
        var keyframe = Math.floor(time / interpolation) + this.animation.offset;
        if (keyframe != this.currentKeyframe) {
            this.mesh.morphTargetInfluences[this.lastKeyframe] = 0;
            this.mesh.morphTargetInfluences[this.currentKeyframe] = 1;
            this.mesh.morphTargetInfluences[keyframe] = 0;
            this.lastKeyframe = this.currentKeyframe;
            this.currentKeyframe = keyframe;
        }
        this.mesh.morphTargetInfluences[keyframe] = (time % interpolation) / interpolation;
        this.mesh.morphTargetInfluences[this.lastKeyframe] = 1 - this.mesh.morphTargetInfluences[keyframe];
    }
}
GT.Character.prototype.talk = function(message) {
	this.conteneurTxt.write(this.name, message);
}
